package server

import (
	"context"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

// Server 表示WebSocket服务器
type Server struct {
	addr           string
	clientsManager *ClientsManager
	upgrader       websocket.Upgrader
	server         *http.Server
	wg             sync.WaitGroup
	quit           chan struct{}
	ctx            context.Context
	cancel         context.CancelFunc
}

// NewServer 创建一个新的服务器实例
func NewServer(addr string) *Server {
	ctx, cancel := context.WithCancel(context.Background())
	return &Server{
		addr:           addr,
		clientsManager: NewClientsManager(),
		upgrader: websocket.Upgrader{
			ReadBufferSize:  1024,
			WriteBufferSize: 1024,
			CheckOrigin: func(r *http.Request) bool {
				return true // 允许所有跨域请求，生产环境中应该更严格
			},
		},
		quit:   make(chan struct{}),
		ctx:    ctx,
		cancel: cancel,
	}
}

// Start 启动WebSocket服务器并监听连接
func (s *Server) Start() error {
	mux := http.NewServeMux()
	mux.HandleFunc("/meeting", s.handleWebSocket)

	s.server = &http.Server{
		Addr:    s.addr,
		Handler: mux,
	}

	log.Printf("WebSocket Server starting on %s", s.addr)

	// 升级HTTP连接为WebSocket连接
	go func() {
		if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("Server failed to start: %v", err)
		}
	}()

	// 等待服务器关闭信号
	<-s.quit
	s.wg.Wait()

	return nil
}

// Stop 停止服务器
func (s *Server) Stop() {
	s.cancel()
	close(s.quit)

	if s.server != nil {
		ctx, cancel := context.WithTimeout(context.Background(), defaultShutdownTimeout)
		defer cancel()
		s.server.Shutdown(ctx)
	}

	s.clientsManager.Close()
	s.wg.Wait()
}

// handleWebSocket 处理WebSocket连接请求
func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
	// 升级HTTP连接为WebSocket连接
	conn, err := s.upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("Failed to upgrade connection: %v", err)
		return
	}

	// 创建客户端
	client := NewClient(conn)
	s.clientsManager.AddClient(client)

	// 处理客户端连接
	s.wg.Add(1)
	go func() {
		defer s.wg.Done()
		s.handleClient(client)
	}()
}

// handleClient 处理客户端连接
func (s *Server) handleClient(client *Client) {
	// 创建一个带有超时的上下文，用于处理客户端连接
	ctx, cancel := context.WithCancel(s.ctx)
	defer cancel()

	// 发送连接成功消息
	connectInfo := ConnectInfo{UserID: client.id}
	netData := NetData{
		Cmd:  int(ConnectSuccess),
		Data: ToJSON(connectInfo),
	}
	client.Send(netData)

	log.Printf("Client connected: %s from %s", client.id, client.RemoteAddr())

	// 启动接收消息的goroutine
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			case <-s.ctx.Done():
				return
			default:
				netData, err := client.Receive()
				if err != nil {
					// 检查是否是正常关闭
					if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
						log.Printf("Client disconnected normally: %s", client.id)
					} else {
						log.Printf("Client disconnected with error: %s, error: %v", client.id, err)
					}
					s.clientsManager.ClientClosed(client)
					cancel()
					return
				}

				// 处理消息
				s.handleMessage(client, netData)
			}
		}
	}()

	// 等待上下文取消
	<-ctx.Done()
}

// handleMessage 处理客户端消息
func (s *Server) handleMessage(client *Client, data NetData) {
	cmd := Command(data.Cmd)

	switch cmd {
	case JoinRoom:
		var joinInfo JoinRoomInfo
		if err := FromJSON(data.Data, &joinInfo); err != nil {
			log.Printf("Failed to parse JoinRoom data: %v", err)
			return
		}
		s.clientsManager.JoinRoom(joinInfo, client)

	case RoomRequestRoomOwner:
		s.clientsManager.RequestRoomOwner(client, data.Data)

	case RoomResponceRoomOwnerRequest:
		s.clientsManager.ResponceRoomOwnerReq(client, data.Data)

	case RoomChangePlayRoomFile:
		s.clientsManager.ChangeRoomPlayFile(client, data.Data)

	case RoomChangePlayState:
		s.clientsManager.ChangeRoomPlayState(client, data.Data)

	case RoomObjSynchronization:
		s.clientsManager.RoomObjSynchronization(client, data.Data)

	case RoomGetRoomPlayProgress:
		s.clientsManager.GetRoomPlayProgress(client, data.Data)

	case RoomSyncRoomPlayProgress:
		s.clientsManager.SyncRoomPlayProgress(client, data.Data)

	case RoomMultiObjSync:
		s.clientsManager.RoomMultiObjSync(client, data.Data)
	case RoomRoomOwnerForcibly:
		s.clientsManager.RoomRoomOwnerForcibly(client, data.Data)

	default:
		log.Printf("Unknown command: %d", data.Cmd)
	}
}

// 默认服务器关闭超时时间
const defaultShutdownTimeout = 2 * time.Second
